package com.letoken.platform.repo.admin.redis;

import com.letoken.platform.pub.constant.RedisKeyConstant;
import com.letoken.platform.pub.util.BeanUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;

import java.util.*;

/**
 *
 * @version 1.0
 * @author： letokenlabs
 * @date： 2021/11/24 9:55
 */
public class AppChainNodeRedis {
    private StringRedisTemplate stringRedisTemplate;

    private AppChainNodeRedis() {
        stringRedisTemplate = BeanUtil.getBean(StringRedisTemplate.class);
    }

    private enum Singleton {
        INSTANCE;
        private AppChainNodeRedis instance;

        // JVM会保证此方法绝对只调用一次
        Singleton() {
            instance = new AppChainNodeRedis();
        }
    }

    /**
     * 活动配置
     */
    String cacheKey = RedisKeyConstant.WEB3_CHAIN_NODE_SPEED;

    public static AppChainNodeRedis me() {
        return AppChainNodeRedis.Singleton.INSTANCE.instance;
    }

    public void set(String chain, String nodeId, Integer speed) {
        stringRedisTemplate.opsForZSet().add(cacheKey + ":" + chain, nodeId, speed);
    }

    /**
     * 获取响应速度最快的节点
     *
     * @param chain
     * @return
     */
    public String getQuickNode(String chain) {

        Set zSet = stringRedisTemplate.opsForZSet().rangeWithScores(cacheKey + ":" + chain, 0, 1);
        if (!zSet.isEmpty()) {
            Optional<ZSetOperations.TypedTuple<String>> value = zSet.stream().findFirst();
            return value != null && value.isPresent() ? value.get().getValue() : null;
        }
        return null;
    }


    Map<String, LinkedList<String>> nodeMap = new HashMap<String, LinkedList<String>>();

    /**
     * 轮询获取节点
     *
     * @param chain
     * @param size  轮询节点数量
     * @return
     */
    public String getNodeByRoundRobin(String chain, int size) {

        LinkedList<String> nodes = nodeMap.containsKey(chain) ? nodeMap.get(chain) : new LinkedList<>();
        if (nodes.isEmpty()) {
            Set zSet = stringRedisTemplate.opsForZSet().rangeWithScores(cacheKey + ":" + chain, 0, size);
            if (!zSet.isEmpty()) {
                Iterator<ZSetOperations.TypedTuple<String>> iterator = zSet.iterator();
                while (iterator.hasNext()) {
                    ZSetOperations.TypedTuple<String> typedTuple = iterator.next();
                    String value = typedTuple.getValue();
                    nodes.push(value);
                }
                nodeMap.put(chain, nodes);
            }
        }

        return nodes.isEmpty() ? getQuickNode(chain) : nodes.pop();
    }
}
